<template>
	<view class="xt__verify-code">
		<!-- 输入框 -->
		<input
			id="xt__input"
			:value="code"
			class="xt__input"
			:focus="isFocus"
			:password="isPassword"
			:type="inputType"
			:maxlength="size"
			@input="input"
			@focus="inputFocus"
			@blur="inputBlur"
		/>

		<!-- 光标 -->
		<view
			id="xt__cursor"
			v-if="cursorVisible && type !== 'middle'"
			class="xt__cursor"
			:style="{ left: codeCursorLeft[code.length] + 'px', height: cursorHeight + 'px', backgroundColor: cursorColor }"
		></view>

		<!-- 输入框 - 组 -->
		<view id="xt__input-ground" class="xt__input-ground">
			<template v-for="(item, index) in size">
				<view
					:key="index"
					:style="{ borderColor: code.length === index && cursorVisible ? boxActiveColor : boxNormalColor }"
					:class="['xt__box', `xt__box-${type + ''}`, `xt__box::after`]"
				>
					<view :style="{ borderColor: boxActiveColor }" class="xt__middle-line" v-if="type === 'middle' && !code[index]"></view>
					<text class="xt__code-text">{{ code[index] | codeFormat(isPassword) }}</text>
				</view>
			</template>
		</view>
	</view>
</template>
<script>
/**
 * @description 输入验证码组件
 * @property {string} type = [box|middle|bottom] - 显示类型 默认：box -eg:bottom
 * @property {string} inputType = [text|number] - 输入框类型 默认：number -eg:number
 * @property {number} size = [4|6] - 支持的验证码数量 默认：6 -eg:6
 * @property {boolean} isFocus - 是否立即聚焦 默认：true
 * @property {boolean} isPassword - 是否以密码形式显示 默认false -eg:false
 * @property {string} cursorColor - 光标颜色 默认：#cccccc
 * @property {string} boxNormalColor - 光标未聚焦到的框的颜色 默认：#cccccc
 * @property {string} boxActiveColor - 光标聚焦到的框的颜色 默认：#000000
 * @event {Function(data)} confirm - 输入完成
 */
export default {
	name: 'xt-verify-code',
	props: {
		value: {
			type: String,
			default: () => ''
		},
		type: {
			type: String,
			default: () => 'box'
		},
		inputType: {
			type: String,
			default: () => 'number'
		},
		size: {
			type: Number,
			default: () => 6
		},
		isFocus: {
			type: Boolean,
			default: () => true
		},
		isPassword: {
			type: Boolean,
			default: () => false
		},
		cursorColor: {
			type: String,
			default: () => '#cccccc'
		},
		boxNormalColor: {
			type: String,
			default: () => '#cccccc'
		},
		boxActiveColor: {
			type: String,
			default: () => '#000000'
		}
	},
	model: {
		prop: 'value',
		event: 'input'
	},
	data() {
		return {
			cursorVisible: false,
			cursorHeight: 35,
			code: '', // 输入的验证码
			codeCursorLeft: [] // 向左移动的距离数组
		};
	},
	created() {
		this.cursorVisible = this.isFocus;
	},
	mounted() {
		this.init();
	},
	methods: {
		/**
		 * @description 初始化
		 */
		init() {
			this.getCodeCursorLeft();
			this.setCursorHeight();
		},
		/**
		 * @description 获取元素节点
		 * @param {string} elm - 节点的id、class 相当于 document.querySelect的参数 -eg: #id
		 * @param {string} type = [single|array] - 单个元素获取多个元素 默认是单个元素
		 * @param {Function} callback - 回调函数
		 */
		getElement(elm, type = 'single', callback) {
			uni
				.createSelectorQuery()
				.in(this)
				[type === 'array' ? 'selectAll' : 'select'](elm)
				.boundingClientRect()
				.exec(data => {
					callback(data[0]);
				});
		},
		/**
		 * @description 计算光标的高度
		 */
		setCursorHeight() {
			this.getElement('.xt__box', 'single', boxElm => {
				this.cursorHeight = boxElm.height * 0.6;
			});
		},
		/**
		 * @description 获取光标在每一个box的left位置
		 */
		getCodeCursorLeft() {
			// 获取父级框的位置信息
			this.getElement('#xt__input-ground', 'single', parentElm => {
				const parentLeft = parentElm.left;
				// 获取各个box信息
				this.getElement('.xt__box', 'array', elms => {
					this.codeCursorLeft = [];
					elms.forEach(elm => {
						this.codeCursorLeft.push(elm.left - parentLeft + elm.width / 2);
					});
				});
			});
		},

		// 输入框输入变化的回调
		input(e) {
			const value = e.detail.value;
			this.cursorVisible = value.length !== this.size;
			this.$emit('input', value);
			this.inputSuccess(value);
		},

		// 输入完成回调
		inputSuccess(value) {
			if (value.length === this.size) {
				this.$emit('confirm', value);
			}
		},
		// 输入聚焦
		inputFocus() {
			this.cursorVisible = this.code.length !== this.size;
		},
		// 输入失去焦点
		inputBlur() {
			this.cursorVisible = false;
		}
	},
	watch: {
		value(val) {
			this.code = val;
		}
	},
	filters: {
		codeFormat(val, isPassword) {
			let value = '';
			if (val) {
				value = isPassword ? '*' : val;
			}
			return value;
		}
	}
};
</script>
<style lang="scss" scoped>
.xt__verify-code {
	position: relative;
	width: 100%;
	box-sizing: border-box;

	.xt__input {
		height: 100%;
		width: 200%;
		position: absolute;
		left: -100%;
		z-index: 1;
	}
	.xt__cursor {
		position: absolute;
		top: 50%;
		transform: translateY(-50%);
		display: inline-block;
		width: 2px;
		animation-name: cursor;
		animation-duration: 0.8s;
		animation-iteration-count: infinite;
	}

	.xt__input-ground {
		display: flex;
		justify-content: space-between;
		align-items: center;
		width: 100%;
		box-sizing: border-box;
		.xt__box {
			position: relative;
			display: inline-block;
			width: 76rpx;
			height: 112rpx;
			&-bottom {
				border-bottom-width: 2px;
				border-bottom-style: solid;
			}

			&-box {
				border-width: 2px;
				border-style: solid;
			}

			&-middle {
				border: none;
			}

			.xt__middle-line {
				position: absolute;
				top: 50%;
				left: 50%;
				width: 50%;
				transform: translate(-50%, -50%);
				border-bottom-width: 2px;
				border-bottom-style: solid;
			}

			.xt__code-text {
				position: absolute;
				top: 50%;
				left: 50%;
				font-size:52rpx;
				transform: translate(-50%, -50%);
			}
		}
	}
}

@keyframes cursor {
	0% {
		opacity: 1;
	}

	100% {
		opacity: 0;
	}
}
</style>
